package org.atf.command;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.Callable;

import org.apache.log4j.Logger;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;

public class AtfRemote extends JSch{
	private String hostname;
	private String username;
	private String password;
	private int port=22;
	private String knownHostsFilename;
	private StringBuffer outputBuffer;
	private StringBuffer errorBuffer;
	private int errorCode=-1;
	private boolean running = false;
	private String command;
	private Map<String, String> env;
	private String arguments;
	private final static String CURRENT_DIRECTORY = ".";
	private final static String PARENT_DIRECTORY = "..";
	private  static Logger  logger = Logger.getLogger(AtfRemote.class);
	public AtfRemote(){
		super();
	}

	public AtfRemote(String hName, String uName, String pwd, int port){
		super();	
		this.hostname=hName;
		this.username=uName;
		this.password=pwd;
		this.port=port;
		knownHostsFilename=System.getProperty("user.home")+"/.ssh/known_hosts";
		initialize();
	}
	
	public AtfRemote(String hName, String uName, String pwd, int port, String knownHostsFilename){
		super();	
		this.hostname=hName;
		this.username=uName;
		this.password=pwd;
		this.port=port;
		if(knownHostsFilename != null && !knownHostsFilename.equalsIgnoreCase("")){
			this.knownHostsFilename=knownHostsFilename;
		}else{
			this.knownHostsFilename=System.getProperty("user.home")+"/.ssh/known_hosts";
		}
		initialize();
		
	}
	public void initialize(){
		if(knownHostsFilename != null && ! knownHostsFilename.equalsIgnoreCase("")){
			System.out.println("knownHostFile: "+knownHostsFilename);
			try {
				setKnownHosts(knownHostsFilename);
			} catch (JSchException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	public boolean testSSHConnection() throws JSchException{
		 boolean result=false;
		Session session=getSession(username,hostname,port);
		session.setPassword(password);
		session.setConfig("StrictHostKeyChecking", "no");
		session.connect();
		if(session.isConnected()){
			result=true;
		}
		session.disconnect();
		 return result;
	 }
	public File put(String from, String to) throws JSchException, SftpException{
		File resultFile= null;
			Session session=getSession(username,hostname,port);
			session.setPassword(password);
			session.setConfig("StrictHostKeyChecking", "no");
			session.connect();
			Channel channel = session.openChannel("sftp");
			channel.connect();
			ChannelSftp sftpChannel = (ChannelSftp) channel;
            sftpChannel.put(from, to);
            sftpChannel.exit();
            channel.disconnect();
            session.disconnect();
		return resultFile;
	}
	
	public File getFile(String from, String to) throws JSchException, SftpException{
		File resultFile= null;
			Session session=getSession(username,hostname,port);
			session.setPassword(password);
			session.setConfig("StrictHostKeyChecking", "no");
			session.connect();
			Channel channel = session.openChannel("sftp");
			channel.connect();
			ChannelSftp sftpChannel = (ChannelSftp) channel;
            sftpChannel.get(from, to);
            sftpChannel.exit();
            channel.disconnect();
    		session.disconnect();
            File localFile= new File(to);
            if(localFile.exists()){
            	resultFile= localFile;
            }
            
		return resultFile;
	}
	public List<String> getFileList(String filPath) throws JSchException, SftpException{
		List<String> fileList= new ArrayList<String>();
			Session session=getSession(username,hostname,port);
			session.setPassword(password);
			session.setConfig("StrictHostKeyChecking", "no");
			session.connect();
			Channel channel = session.openChannel("sftp");
			channel.connect();
			ChannelSftp sftpChannel = (ChannelSftp) channel;
			//sftpChannel.cd(filPath);
			Vector<LsEntry> v  = sftpChannel.ls(filPath);
			for (LsEntry i : v){
				if (!i.getAttrs().isDir()){
					String file = new String(i.getFilename());
					if (!file.equals(CURRENT_DIRECTORY) || !file.equals(PARENT_DIRECTORY)){
						
						fileList.add(i.getFilename());
					}
				}
					
			}
			sftpChannel.exit();
			channel.disconnect();
			session.disconnect();
		return fileList;
	}
	/**
     * @param filename File name
     * @return Number of seconds since the file was written to
     * @throws IOException If an error occurs
     */
    public long getLastModifiedTime(String filename) throws JSchException, SftpException
    {
   
        	Session session=getSession(username,hostname,port);
        	session.setPassword(password);
    		session.setConfig("StrictHostKeyChecking", "no");
    		session.connect();
    		Channel channel = session.openChannel("sftp");
    		channel.connect();
    		ChannelSftp sftpChannel = (ChannelSftp) channel;
            SftpATTRS attrs = sftpChannel.stat("./" + filename);
            sftpChannel.exit();
            channel.disconnect();
    		session.disconnect();
            return attrs.getMTime() * 1000L;
    
        
    }
    /**
     * Creates a directory
     * 
     * @param directoryName The directory name
     * @throws IOException If an error occurs
     * @throws JSchException 
     * @throws SftpException 
     */
    public void mkdir(String directoryName) throws IOException, JSchException, SftpException
    {
      
        	Session session=getSession(username,hostname,port);
        	session.setPassword(password);
    		session.setConfig("StrictHostKeyChecking", "no");
    		session.connect();
    		Channel channel = session.openChannel("sftp");
    		channel.connect();
    		ChannelSftp sftpChannel = (ChannelSftp) channel;
    		File file= new File(directoryName);
    		String parentDir= file.getParent();
    		sftpChannel.cd(parentDir);
    		sftpChannel.mkdir(file.getName());
    		sftpChannel.exit();
    		channel.disconnect();
    		session.disconnect();
     
    }
    public boolean deleteIfDirExists(String path) throws JSchException {
		// TODO Auto-generated method stub
    	boolean result= false;
    	Session session=getSession(username,hostname,port);
    	session.setPassword(password);
		session.setConfig("StrictHostKeyChecking", "no");
		session.connect();
		Channel channel = session.openChannel("sftp");
		channel.connect();
		ChannelSftp sftpChannel = (ChannelSftp) channel;
		
		try
        {   
			File file= new File(path);
			String parentDir= file.getParent();
			sftpChannel.cd(parentDir);      
			sftpChannel.rmdir(file.getName());
			result = true;
        }catch (SftpException e)
        {
            result= false;
        }
		sftpChannel.exit();
		channel.disconnect();
		session.disconnect();
    	return result;
	}
    
    public boolean deleteFileIfexists(String filePath) throws JSchException {
  		// TODO Auto-generated method stub
      	boolean result= false;
      	Session session=getSession(username,hostname,port);
      	session.setPassword(password);
  		session.setConfig("StrictHostKeyChecking", "no");
  		session.connect();
  		Channel channel = session.openChannel("sftp");
  		channel.connect();
  		ChannelSftp sftpChannel = (ChannelSftp) channel;
  		
  		try
          {   
  			File file= new File(filePath);
  			String parentDir= file.getParent();
  			sftpChannel.cd(parentDir);      
  			sftpChannel.rmdir(file.getName());
  			result = true;
          }catch (SftpException e)
          {
              result= false;
          }
  		sftpChannel.exit();
  		channel.disconnect();
  		session.disconnect();
      	return result;
  	}
    
    public boolean  checkIfDirExists(String path) throws JSchException{
    	Session session=getSession(username,hostname,port);
    	session.setPassword(password);
		session.setConfig("StrictHostKeyChecking", "no");
		session.connect();
		Channel channel = session.openChannel("sftp");
		channel.connect();
		ChannelSftp sftpChannel = (ChannelSftp) channel;	
		boolean result= false;
		try
        {         
			sftpChannel.cd(path);
			result = true;
        }catch (SftpException e)
        {
            result= false;
        }
		sftpChannel.exit();
		channel.disconnect();
		session.disconnect();
		return result;
    }
    public void createDirIfNotExists(String path) throws IOException, JSchException{
    	Session session=getSession(username,hostname,port);
    	session.setPassword(password);
		session.setConfig("StrictHostKeyChecking", "no");
		session.connect();
		Channel channel = session.openChannel("sftp");
		channel.connect();
		ChannelSftp sftpChannel = (ChannelSftp) channel;		
		try
        {         
			sftpChannel.cd(path);
        }
        catch (SftpException e)
        {
            String message = "Error '" + e.getMessage() + "' occurred when trying to CDW to '" + path + "'.";
            logger.error(message);
            try {
				mkdir(path);
			} catch (SftpException e1) {
				// TODO Auto-generated catch block
				throw new IOException(e1.getMessage());
			}
        }
		sftpChannel.exit();
		channel.disconnect();
		session.disconnect();
		
    }

	
	public List<String> getDirectoryList(String dirPath) throws JSchException, SftpException{
		List<String> dirList= new ArrayList<String>();
		Session session=getSession(username,hostname,port);
		session.setPassword(password);
		session.setConfig("StrictHostKeyChecking", "no");
		session.connect();
		Channel channel = session.openChannel("sftp");
		channel.connect();
		ChannelSftp sftpChannel = (ChannelSftp) channel;
		//sftpChannel.cd(dirList);
		Vector<LsEntry> v  = sftpChannel.ls(dirPath);
		for (LsEntry i : v){
			if (i.getAttrs().isDir()){
				if (!i.getFilename().equals(CURRENT_DIRECTORY) && !i.getFilename().equals(PARENT_DIRECTORY))
                {
					dirList.add(i.getFilename());
                }
				
			}
				
		}
		sftpChannel.exit();
		channel.disconnect();
		session.disconnect();
		return dirList;
		
	}
	
	public void remoteExecute() throws JSchException, IOException{
		initialize();
		running=true;
		Session session=getSession(username,hostname,port);
		session.setPassword(password);
		session.setConfig("StrictHostKeyChecking", "no");
		session.connect();
		logger.info("Connected OK");
		ChannelExec channel=(ChannelExec)session.openChannel("exec");
		if(env != null){
			Iterator keys=env.keySet().iterator();
			while(keys.hasNext()){
				String name=(String)keys.next();
				String value=env.get(name);
				logger.debug("Name: "+name+" /Value: "+value);
				channel.setEnv(name,value);
			}
		}
		if(arguments != null && !arguments.equalsIgnoreCase("")){
			command+=" "+arguments;
		}
		logger.info("cmd: "+command);
		channel.setCommand(command);
		InputStream in=channel.getInputStream();
		InputStream err=channel.getErrStream();	
		channel.connect();
		byte[] tmp=new byte[1024];
		while(true){
			while(in.available() > 0){
				int i=in.read(tmp,0,1024);
				if(i<0) break;
				getOutputBuffer().append(new String(tmp,0,i));
			}
			while(err.available() > 0){
				int i=err.read(tmp,0,1024);
				if(i<0) break;
				getErrorBuffer().append(new String(tmp,0,i));
			}
			if(channel.isClosed()){
				setErrorCode(channel.getExitStatus());
				break;
			}
			try{Thread.sleep(1000);}catch(Exception e){};
			
		}
		setErrorCode(channel.getExitStatus());
		
		channel.disconnect();
		session.disconnect();
		running=false;
	}
	
	public void remoteExecute(String cmd, Map<String, String> env,String arguments) throws JSchException, IOException{
		running=true;
		Session session=getSession(username,hostname,port);
		session.setPassword(password);
		session.setConfig("StrictHostKeyChecking", "no");
		session.connect();
		logger.info("Connected OK");
		ChannelExec channel=(ChannelExec)session.openChannel("exec");
		if(env != null){
			Iterator keys=env.keySet().iterator();
			while(keys.hasNext()){
				String name=(String)keys.next();
				String value=env.get(name);
				logger.debug("Name: "+name+" /Value: "+value);
				channel.setEnv(name,value);
			}
		}
		if(arguments != null && !arguments.equalsIgnoreCase("")){
			cmd+=" "+arguments;
		}
		logger.info("cmd: "+cmd);
		channel.setCommand(cmd);
		InputStream in=channel.getInputStream();
		InputStream err=channel.getErrStream();	
		channel.connect();
		byte[] tmp=new byte[1024];
		while(true){
			while(in.available() > 0){
				int i=in.read(tmp,0,1024);
				if(i<0) break;
				getOutputBuffer().append(new String(tmp,0,i));
			}
			while(err.available() > 0){
				int i=err.read(tmp,0,1024);
				if(i<0) break;
				getErrorBuffer().append(new String(tmp,0,i));
			}
			if(channel.isClosed()){
				setErrorCode(channel.getExitStatus());
				break;
			}
			try{Thread.sleep(1000);}catch(Exception e){};
			
		}
		setErrorCode(channel.getExitStatus());
		channel.disconnect();
		session.disconnect();
		running=false;
		
	}

	
	public StringBuffer getOutputBuffer() {
	
		
		if(outputBuffer == null){
			outputBuffer = new StringBuffer();
		}	 
	        return outputBuffer;
	}

	public void setOutputBuffer(StringBuffer outputBuffer) {
		this.outputBuffer = outputBuffer;
	}

	public StringBuffer getErrorBuffer() {
		if(errorBuffer == null)
			errorBuffer = new StringBuffer("");
        return errorBuffer;
	}

	public void setErrorBuffer(StringBuffer errorBuffer) {
		this.errorBuffer = errorBuffer;
	}

	public int getErrorCode() {
		return errorCode;
	}

	public void setErrorCode(int errorCode) {
		this.errorCode = errorCode;
	}
	public void clearOutput(){
		outputBuffer = null;
	}
	public void clearError(){
		errorBuffer = null;
	}

	public String getHostname() {
		return hostname;
	}

	public void setHostname(String hostname) {
		this.hostname = hostname;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public String getKnownHostsFilename() {
		return knownHostsFilename;
	}

	public void setKnownHostsFilename(String knownHostsFilename) {
		this.knownHostsFilename = knownHostsFilename;
	}

	public String getCommand() {
		return command;
	}

	public void setCommand(String command) {
		this.command = command;
	}

	public Map<String, String> getEnv() {
		return env;
	}

	public void setEnv(Map<String, String> env) {
		this.env = env;
	}

	public String getArguments() {
		return arguments;
	}

	public void setArguments(String arguments) {
		this.arguments = arguments;
	}

	public boolean isRunning() {
		return running;
	}

	public void setRunning(boolean running) {
		this.running = running;
	}

	


	
	
	
	
}
